#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <unordered_map>
#include <sstream>
#include <fstream>

using namespace std;

static const string sep = "\r\n";
static const string hsep = ": ";
static const string space = " ";
static const std::string httpversion = "HTTP/1.0";
static const string wwwroot = "wwwroot";
static const string homepage = "index.html";
static const string Suffixsep = ".";
static const string Args_sep = "?";

class HttpRequest;
class HttpResponse;
using func_t = function<shared_ptr<HttpResponse>(shared_ptr<HttpRequest>)>;

class HttpRequest
{
private:
    std::string GetOneline(std::string &reqstr)
    {
        if (reqstr.empty())
            return reqstr;
        auto pos = reqstr.find(sep);
        if (pos == std::string::npos)
            return std::string();

        std::string line = reqstr.substr(0, pos);
        reqstr.erase(0, pos + sep.size());
        return line.empty() ? sep : line;
    }
    bool ParseHeanderhelp(string &line, string *k, string *v)
    {
        auto pos = line.find(hsep);
        if (pos == string::npos)
            return false;
        *k = line.substr(0, pos);
        *v = line.substr(pos + hsep.size());
        return true;
    }

public:
    HttpRequest() : _blank_line(sep), _path(wwwroot)
    {
    }
    void DeSerialize(string &req_str)
    {
        _req_line = GetOneline(req_str);
        while (true)
        {
            string line = GetOneline(req_str);
            if (line.empty())
                break;
            else if (line == sep)
            {
                _req_text = req_str;
                break;
            }
            else
            {
                _req_header.emplace_back(req_str);
            }
        }
        ParseReqline();
        ParseHeader();
    }
    void Print()
    {
        std::cout << "===" << _req_line << std::endl;
        for (auto &header : _req_header)
        {
            std::cout << "***" << header << std::endl;
        }
        std::cout << _blank_line;
        std::cout << _req_text << std::endl;

        std::cout << "method ### " << _method << std::endl;
        std::cout << "path ### " << _path << std::endl;
        std::cout << "httpverion ### " << _version << std::endl;
        for (auto &header : _headerkv)
        {
            std::cout << "@@@" << header.first << " - " << header.second << std::endl;
        }
    }
    bool ParseReqline()
    {
        if (_req_line.empty())
            return false;
        stringstream ss(_req_line);
        ss >> _method >> _url >> _version;
        // 进行处理比如:/index.html/?user=zhangsan&passwd=123456
        //
        if (strcasecmp("get", _method.c_str()) == 0)
        {
            auto argspos = _url.find(Args_sep);
            if (argspos != string ::npos)
            {
                Log(INFO, "change begin, url: %s", _url.c_str());
                _args = _url.substr(argspos + Args_sep.size());
                _url.resize(argspos);
                Log(INFO, "change done, url: %s, _args: %s", _url.c_str(), _args.c_str());
            }
        }
        _path += _url;
        Log(DEBUG, "url: %s", _url.c_str());
        if (_path[_path.size() - 1] == '/')
        {
            _path += homepage;
        }
        auto pos = _path.rfind(Suffixsep); // 倒着遍历
        if (pos == string ::npos)
        {
            _suffix = ".unknown";
        }
        else
        {
            _suffix = _path.substr(pos);
        }
        Log(INFO, "client wang get %s", _path.c_str());
        return true;
    }
    bool ParseHeader()
    {
        for (auto &header : _req_header)
        {
            string k, v;
            if (ParseHeanderhelp(header, &k, &v))
            {
                _headerkv.insert(make_pair(k, v));
            }
        }
        return true;
    }
    bool IsExec()
    {
        return !_args.empty() || !_req_text.empty();
    }
    string Args()
    {
        return _args;
    }
    string Text()
    {
        return _req_text;
    }
    string Method()
    {
        return _method;
    }
    string path()
    {
        return _path;
    }
    string Suffix()
    {
        return _suffix;
    }

private:
    string _req_line;
    vector<string> _req_header;
    string _blank_line;
    string _req_text;
    string _suffix;

    // 理想解析结果
    string _method;
    string _args; // 参数
    string _url;  // 网络资源/.......
    string _path; // 网络路径
    unordered_map<string, string> _headerkv;
    string _version;
};
class HttpResponse
{
public:
    HttpResponse() : _version(httpversion), _blank_line(sep)
    {
    }
    void AddStauasline(int code, const string &desc)
    {
        _code = code;
        _desc = desc;
    }
    void AddStauasline(int code)
    {
        _code = code;
        _desc = "ok";
    }
    void AddHeader(const string &k, const string &v)
    {
        Log(DEBUG, "AddHeader: %s->%s", k.c_str(), v.c_str());
        _headers[k] = v;
    }
    void AddText(const string &text)
    {
        _resp_text = text;
    }
    string Serialize()
    {
        string ret = _version + space + to_string(_code) + space + _desc + sep;
        for (auto &header : _headers)
        {
            _resp_header.emplace_back(header.first + hsep + header.second + sep);
        }
        for (auto &resp : _resp_header)
        {
            ret += resp;
        }
        ret += _blank_line;
        ret += _resp_text;
        return ret;
    }
    ~HttpResponse() {}

private:
    //
    int _code;
    string _version;
    string _desc;
    unordered_map<string, string> _headers;

    // 应答格式
    string _status_line;
    vector<string> _resp_header;
    string _blank_line;
    string _resp_text;
};
class Factor
{

public:
    static shared_ptr<HttpRequest> BuildRequest()
    {
        return make_shared<HttpRequest>();
    }
    static shared_ptr<HttpResponse> BuildResponse()
    {
        return make_shared<HttpResponse>();
    }
};
class HttpServer
{
public:
    HttpServer()
    {
        mime_type.insert(make_pair(".html", "text/html"));
        mime_type.insert(make_pair(".css", "text/css"));

        _code_desc.insert(make_pair(100, "Continue"));
        _code_desc.insert(make_pair(200, "OK"));
        _code_desc.insert(make_pair(301, "Moved Permanently"));
        _code_desc.insert(make_pair(302, "Found"));
        _code_desc.insert(make_pair(404, "Not Found"));
        _code_desc.insert(make_pair(500, "Internal Server Error"));
    }
    void AddHandler(const std::string functionname, func_t f)
    {
        std::string key = wwwroot + functionname; // wwwroot/login
        _funcs[key] = f;
    }
    std::string ReadFileContent(const std::string &path, int *size)
    {
        // 要按照二进制打开
        std::ifstream in(path, std::ios::binary);
        if (!in.is_open())
        {
            return std::string();
        }
        in.seekg(0, in.end);
        int filesize = in.tellg();
        in.seekg(0, in.beg);

        std::string content;
        content.resize(filesize);
        in.read((char *)content.c_str(), filesize);
        in.close();
        *size = filesize;
        return content;
    }
    // #define TEST 1
    string HttpHander(string request)
    {
#ifdef TEST
        cout << "--------------------------";
        cout << request;
        string response = "http/1.0 200 OK\r\n";
        response += "\r\n";
        response += "<html><body>hello world,hello hw!</h1></body></html>";
        return response;
#else
        shared_ptr<HttpRequest> req = Factor::BuildRequest();
        req->DeSerialize(request);

        if (req->IsExec())
        {
            cout << "1" << endl;
            auto response = _funcs[req->path()](req);
            return response->Serialize();
        }
        else
        {
            cout << "2" << endl;
            auto resp = Factor::BuildResponse();
            int code = 200;
            int contentsize = 0;
            string text = ReadFileContent(req->path(), &contentsize);
            if (text.empty())
            {
                cout << "2.1" << endl;
                code = 404;
                // 初始化resp
                resp->AddStauasline(code, _code_desc[code]);
                std::string text404 = ReadFileContent("wwwroot/404.html", &contentsize);
                resp->AddHeader("Content-Length", to_string(contentsize));
                resp->AddHeader("Content-Type", mime_type[".html"]);
                resp->AddText(text404);
            }
            else
            {
                cout << "2.2" << endl;
                string Suffix = req->Suffix();
                resp->AddStauasline(code, _code_desc[code]);
                resp->AddHeader("Content-Length", to_string(contentsize));
                resp->AddText(text);
                resp->AddHeader("Content-Type", mime_type[Suffix]);
            }
            return resp->Serialize();
        }
#endif
    }
    ~HttpServer()
    {
    }

private:
    unordered_map<string, string> mime_type;
    unordered_map<int, string> _code_desc;
    unordered_map<string, func_t> _funcs;
};